cmake_minimum_required(VERSION 3.12)
project(mpi_tests LANGUAGES C CXX Fortran)

# 查找 MPI 包
find_package(MPI REQUIRED)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 禁用从父项目继承的编译选项
if(COMMAND cmake_policy)
    cmake_policy(SET CMP0079 NEW)
endif()

# Fortran 配置
set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules)
include_directories(${CMAKE_Fortran_MODULE_DIRECTORY})

function(set_mpi_compile_options TARGET_NAME LANGUAGE)
    if(LANGUAGE STREQUAL "Fortran")
        if(CMAKE_Fortran_COMPILER_ID MATCHES "GNU")
            target_compile_options(${TARGET_NAME} PRIVATE
                -O3
                -Wall
                -Wno-maybe-uninitialized  # 处理Fortran常见误报
                -Wno-tabs  # 忽略tab字符警告
                -ffree-line-length-none
                -fimplicit-none
                -Wno-error  # 确保警告不会导致编译失败
            )
        elseif(CMAKE_Fortran_COMPILER_ID MATCHES "Intel")
            target_compile_options(${TARGET_NAME} PRIVATE
                -O3 
                -warn all
                -diag-disable 5268,7712
            )
        endif()
    elseif(LANGUAGE MATCHES "CXX|C")
        if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
            target_compile_options(${TARGET_NAME} PRIVATE
                -O3
                -Wall
                -Wextra
                $<$<COMPILE_LANGUAGE:CXX>:-Weffc++>
                $<$<COMPILE_LANGUAGE:CXX>:-Wno-non-virtual-dtor>
            )
        endif()
    endif()
endfunction()

function(add_mpi_fortran_executable TARGET_NAME)
    add_executable(${TARGET_NAME} ${ARGN})
    set_target_properties(${TARGET_NAME} PROPERTIES COMPILE_OPTIONS "")
    target_link_libraries(${TARGET_NAME} PRIVATE MPI::MPI_Fortran)
    set_mpi_compile_options(${TARGET_NAME} "Fortran")
endfunction()

function(add_mpi_cpp_executable TARGET_NAME)
    add_executable(${TARGET_NAME} ${ARGN})
    target_link_libraries(${TARGET_NAME} PRIVATE MPI::MPI_CXX)
    set_mpi_compile_options(${TARGET_NAME} "CXX")
endfunction()

function(add_mpi_fortran_executable TARGET_NAME)
    add_executable(${TARGET_NAME} ${ARGN})
    set_target_properties(${TARGET_NAME} PROPERTIES COMPILE_OPTIONS "")
    target_link_libraries(${TARGET_NAME} PRIVATE MPI::MPI_Fortran)
    set_mpi_compile_options(${TARGET_NAME} "Fortran")
endfunction()

function(add_mpi_cpp_executable TARGET_NAME)
    add_executable(${TARGET_NAME} ${ARGN})
    target_link_libraries(${TARGET_NAME} PRIVATE MPI::MPI_CXX)
    set_mpi_compile_options(${TARGET_NAME} "CXX")
endfunction()

function(add_mpi_mixed_executable TARGET_NAME)
    set(FORTRAN_SOURCES)
    set(C_SOURCES)
    foreach(src ${ARGN})
        if(src MATCHES "\\.(f90|F90|f95|F95)$")
            list(APPEND FORTRAN_SOURCES ${src})
        elseif(src MATCHES "\\.(c|C|cpp|CPP)$")
            list(APPEND C_SOURCES ${src})
        endif()
    endforeach()

    add_executable(${TARGET_NAME} ${FORTRAN_SOURCES} ${C_SOURCES})
    
    target_link_libraries(${TARGET_NAME} PRIVATE 
        MPI::MPI_Fortran 
        MPI::MPI_C
    )
    
    target_include_directories(${TARGET_NAME} PRIVATE
        ${CMAKE_Fortran_MODULE_DIRECTORY}
    )
    
    # 为 Fortran 源文件设置编译选项
    if(FORTRAN_SOURCES)
        set_source_files_properties(${FORTRAN_SOURCES} PROPERTIES
            LANGUAGE Fortran
        )
        if(CMAKE_Fortran_COMPILER_ID MATCHES "GNU")
            set_property(SOURCE ${FORTRAN_SOURCES} APPEND PROPERTY COMPILE_OPTIONS
                "-O3"
                "-Wall"
                "-Wno-maybe-uninitialized"
                "-Wno-tabs"
                "-ffree-line-length-none"
                "-fimplicit-none"
                "-Wno-error"
            )
        elseif(CMAKE_Fortran_COMPILER_ID MATCHES "Intel")
            set_property(SOURCE ${FORTRAN_SOURCES} APPEND PROPERTY COMPILE_OPTIONS
                "-O3"
                "-warn"
                "all"
                "-diag-disable"
                "5268,7712"
            )
        endif()
    endif()
    
    # 为 C 源文件设置编译选项
    if(C_SOURCES)
        set_source_files_properties(${C_SOURCES} PROPERTIES
            LANGUAGE C
        )
        if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
            set_property(SOURCE ${C_SOURCES} APPEND PROPERTY COMPILE_OPTIONS
                "-O3"
                "-Wall"
                "-Wextra"
            )
        endif()
    endif()
endfunction()

add_mpi_cpp_executable(test_mpi_cpp test_mpi.cpp)
add_mpi_cpp_executable(mpi_basic_cpp mpi_basic.cpp)
add_mpi_cpp_executable(mpi_sendrecv_demo mpi_sendrecv_demo.cpp)
add_mpi_cpp_executable(mpi_split_comm_index mpi_split_comm_index.cpp)

add_mpi_cpp_executable(test_all_mpi_cpp test_all_mpi.cpp)

add_mpi_fortran_executable(test_mpi_fortran test_mpi.F90)
add_mpi_fortran_executable(mpi_max_broadcast mpi_max_broadcast.F90)
add_mpi_fortran_executable(mpi_comm_split mpi_comm_split.F90)
add_mpi_fortran_executable(mpi_basic mpi_basic.F90)

add_mpi_mixed_executable(mixed_c_fortran
    mixed_c_fortran.c
    mixed_c_fortran.f90
)

add_mpi_mixed_executable(mixed_fortran_c
    mixed_fortran_c.c
    mixed_fortran_c.f90
)

configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/run_all_mpi_tests.sh
    ${CMAKE_BINARY_DIR}/run_all_mpi_tests.sh
    COPYONLY
)

configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/check_mpi_coverage.py
    ${CMAKE_BINARY_DIR}/check_mpi_coverage.py
    COPYONLY
)

# mkdir build && cd build
# cmake .. && make -j32
# export MPITRACER_LIB=$PWD/

# 安装sudo apt install -y openmpi-bin libopenmpi-dev
# mpifort test_mpi.F90 -o test_mpi_fortran
# mpicxx -o test_mpi_cpp test_mpi.cpp  -lmpi
# export MPITRACER_LIB=mpitracer_path
# mpirun -n 4 ./test_mpi_cpp # 先保证能正常运行
# mpirun -x LD_PRELOAD=$MPITRACER_LIB/mpitracer.so -n 4 ./test_mpi_cpp
# bash run_all_mpi_tests.sh